package dungeonTools;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;

import java.util.HashMap;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import dungeon.CompositeDungeon;
import dungeon.Dungeon;
import dungeonElements.Square;

/**
 * @author Tom Jacobs & Ewoud De Moor
 * @version 3.0
 */
public class KDTreeTest {
	public static Dungeon<Square> dungeon1 ;
	public static Coordinate dungeon1Coordinate;
	public static Dungeon<Square> dungeon2 ;
	public static Coordinate dungeon2Coordinate;
	public static KDTree tree;
	public static HashMap<Dungeon<? extends Square>, Coordinate> allSubDungeons;
	
	@Before
	public void setUp() throws Exception {
		dungeon1 = new CompositeDungeon<Square>(5, 5, 5);
		dungeon1Coordinate = new Coordinate(0, 0, 0);
		dungeon2 = new CompositeDungeon<Square>(5, 5, 5);
		dungeon2Coordinate = new Coordinate(4,4,5);
		tree = new KDTree(dungeon1Coordinate, new Coordinate(9,9,9));
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void testAddDungeonToNode_LegalCase() {
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		allSubDungeons = tree.getAllDungeonsInNodeSubTree();
		assertEquals(1,allSubDungeons.size());
		assertTrue(allSubDungeons.containsKey(dungeon1));
		assertTrue(allSubDungeons.containsValue(dungeon1Coordinate));
		dungeon2Coordinate = new Coordinate(5, 5, 5);
		tree.addDungeonToNode(dungeon2, dungeon2Coordinate);
		allSubDungeons = tree.getAllDungeonsInNodeSubTree();
		assertEquals(2,allSubDungeons.size());
		assertTrue(allSubDungeons.containsKey(dungeon1));
		assertTrue(allSubDungeons.containsValue(dungeon1Coordinate));
		assertTrue(allSubDungeons.containsKey(dungeon2));
		assertTrue(allSubDungeons.containsValue(new Coordinate(5,5,5)));
	}

	@Test(expected = IllegalArgumentException.class)
	public void testAddDungeonToNode_Exception_Overlap() {
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		dungeon2Coordinate = new Coordinate(4, 4, 4);
		tree.addDungeonToNode(dungeon2, dungeon2Coordinate);
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void testAddDungeonToNode_Exception_NoFit1() {
		tree.addDungeonToNode(new CompositeDungeon<Square>(11, 10, 10), dungeon1Coordinate);
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void testAddDungeonToNode_Exception_NoFit2() {
		KDTree tree2 = new KDTree(new Coordinate(1,1,1), new Coordinate(10, 10, 10));
		tree2.addDungeonToNode(new CompositeDungeon<Square>(10, 10, 10), dungeon1Coordinate);
	}
	
	@Test
	public void testAddDungeonToNode_OtherLocationLegalCase() {
		KDTree tree2 = new KDTree(new Coordinate(1,1,1), new Coordinate(10, 10, 10));
		tree2.addDungeonToNode(new CompositeDungeon<Square>(10, 10, 10), new Coordinate(1,1,1));
	}

	@Test
	public void testRemoveDungeonFromSubNode_LegalCase() {
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		dungeon2Coordinate = new Coordinate(5, 5, 5);
		tree.addDungeonToNode(dungeon2, dungeon2Coordinate);
		tree.removeDungeonFromSubNode(dungeon1, dungeon1Coordinate);
		allSubDungeons = tree.getAllDungeonsInNodeSubTree();
		assertEquals(1,allSubDungeons.size());
		assertTrue(allSubDungeons.containsKey(dungeon2));
		assertTrue(allSubDungeons.containsValue(new Coordinate(5,5,5)));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void testRemoveDungeonFromSubNode_Exception1(){
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		tree.removeDungeonFromSubNode(dungeon1, new Coordinate(1,0,0));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void testRemoveDungeonFromSubNode_Exception2(){
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		tree.removeDungeonFromSubNode(dungeon2, dungeon1Coordinate);
	}

	@Test
	public void testCanAddDungeonToNode() {
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		assertTrue(tree.canAddDungeonToNode(dungeon2, new Coordinate(5, 5, 5)));
		assertFalse(tree.canAddDungeonToNode(dungeon2, new Coordinate(4,4,4)));
		assertFalse(tree.canAddDungeonToNode(dungeon2, new Coordinate(1,4,4)));
		assertFalse(tree.canAddDungeonToNode(dungeon2, new Coordinate(0,0,0)));
		assertTrue(tree.canAddDungeonToNode(dungeon2, new Coordinate(0,0,5)));
	}
	
	@Test
	public void hasChildDungeonContainingCoordinate(){
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		assertTrue(tree.hasChildDungeonContainingCoordinate(dungeon1Coordinate));
		assertTrue(tree.hasChildDungeonContainingCoordinate(new Coordinate(4,4,4)));
		assertFalse(tree.hasChildDungeonContainingCoordinate(new Coordinate(4,4,5)));
		tree.addDungeonToNode(dungeon1, new Coordinate(4,4,5));
		assertTrue(tree.hasChildDungeonContainingCoordinate(new Coordinate(4,4,5)));
		tree.removeDungeonFromSubNode(dungeon1, new Coordinate(4,4,5));
		assertFalse(tree.hasChildDungeonContainingCoordinate(new Coordinate(4,4,5)));
		tree.addDungeonToNode(dungeon1, new Coordinate(4,4,5));
		assertTrue(tree.hasChildDungeonContainingCoordinate(new Coordinate(4,4,5)));
	}
	
	@Test
	public void getChildDungeonContainingCoordinate_LegalCase(){
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		tree.addDungeonToNode(dungeon2, dungeon2Coordinate);
		assertSame(dungeon1,tree.getChildDungeonContainingCoordinate(dungeon1Coordinate));
		assertSame(dungeon2,tree.getChildDungeonContainingCoordinate(dungeon2Coordinate));
		assertSame(dungeon1,tree.getChildDungeonContainingCoordinate(new Coordinate(1,1,1)));
		assertSame(dungeon1,tree.getChildDungeonContainingCoordinate(new Coordinate(4,4,4)));
		assertSame(dungeon2,tree.getChildDungeonContainingCoordinate(new Coordinate(5,5,5)));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void getChildDungeonContainingCoordinate_Exception(){
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		assertSame(dungeon1,tree.getChildDungeonContainingCoordinate(dungeon2Coordinate));
	}
	
	@Test
	public void dungeonCanHaveNewSize_LegalCase(){
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		tree.addDungeonToNode(dungeon2, dungeon2Coordinate);
		assertTrue(tree.dungeonCanHaveNewSize(dungeon1, dungeon1Coordinate, 10, 10, 5));
		assertTrue(tree.dungeonCanHaveNewSize(dungeon1, dungeon1Coordinate, 10, 10, 5));
		assertTrue(tree.dungeonCanHaveNewSize(dungeon1, dungeon1Coordinate, 5, 5, 5));
		assertFalse(tree.dungeonCanHaveNewSize(dungeon1, dungeon1Coordinate, 5, 5, 6));
		assertTrue(tree.dungeonCanHaveNewSize(dungeon1, dungeon1Coordinate, 5, 5, 5));
		assertFalse(tree.dungeonCanHaveNewSize(dungeon2, dungeon2Coordinate, 5, 5, 6));
		assertFalse(tree.dungeonCanHaveNewSize(dungeon2, dungeon2Coordinate, 10, 10, 10));
		assertTrue(tree.dungeonCanHaveNewSize(dungeon2, dungeon2Coordinate, 3, 3, 3));
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void dungeonCanHaveNewSize_Exception1(){
		tree.dungeonCanHaveNewSize(dungeon1, dungeon1Coordinate, 5, 5, 5);
	}
	
	@Test(expected = IllegalArgumentException.class)
	public void dungeonCanHaveNewSize_Exception2(){
		tree.addDungeonToNode(dungeon2, dungeon1Coordinate);
		tree.dungeonCanHaveNewSize(dungeon1, dungeon1Coordinate, 5, 5, 5);
	}
	
	@Test(expected = UnsupportedOperationException.class)
	public void removeDungeonFromROOTNODE(){
		assertFalse(tree.hasChildDungeonContainingCoordinate(dungeon1Coordinate));
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		assertTrue(tree.hasChildDungeonContainingCoordinate(dungeon1Coordinate));
		tree.removeDungeonFromSubNode(dungeon1, dungeon1Coordinate);
		assertFalse(tree.hasChildDungeonContainingCoordinate(dungeon1Coordinate));
	}
	
	@Test
	public void clear(){
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		tree.addDungeonToNode(dungeon2, dungeon2Coordinate);
		tree.clear();
		assertTrue(tree.canAddDungeonToNode(dungeon1, dungeon1Coordinate));
		assertTrue(tree.canAddDungeonToNode(dungeon2, dungeon2Coordinate));
		assertFalse(tree.hasChildDungeonContainingCoordinate(dungeon1Coordinate));
		assertEquals(new HashMap<Dungeon<? extends Square>, Coordinate>(),tree.getAllDungeonsInNodeSubTree());
		tree.addDungeonToNode(dungeon1, dungeon1Coordinate);
		assertFalse(tree.canAddDungeonToNode(dungeon1, dungeon1Coordinate));
		assertEquals(1, tree.getAllDungeonsInNodeSubTree().size());
		assertTrue(tree.getAllDungeonsInNodeSubTree().containsKey(dungeon1));
		assertTrue(tree.getAllDungeonsInNodeSubTree().containsValue(dungeon1Coordinate));
	}
}
